/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.refactor;

import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;

import java.util.Collection;

public class InlineUselessVariablesIntention extends BaseIntention implements IntentionAction {
    public InlineUselessVariablesIntention() {
    }

    public String getText() {
        return "Inline useless local variables";
    }

    public boolean isAvailable(Project project, Editor editor, PsiFile psiFile) {
        PsiElement element = getElement(editor, psiFile);
        PsiMethodCallExpression psiMethodCallExpression = (PsiMethodCallExpression)
                getContainingElement(element, PsiMethodCallExpression.class);
        if (psiMethodCallExpression == null) {
            return false;
        }
        PsiExpressionList argumentList = psiMethodCallExpression.getArgumentList();
        PsiExpression[] expressions = argumentList.getExpressions();
        for (int i = 0; i < expressions.length; i++) {
            PsiExpression psiExpression = expressions[i];
            if (psiExpression instanceof PsiReferenceExpression) {
                PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression) psiExpression;
                PsiElement psiElement = psiReferenceExpression.resolve();
                if (psiElement instanceof PsiLocalVariable) {
                    PsiLocalVariable psiLocalVariable = (PsiLocalVariable) psiElement;
                    if (isUselessLocalVariable(psiLocalVariable))
                        return true;
                }
            }
        }
        return false;
    }

    public void invoke(Project project, Editor editor, PsiFile psiFile) throws IncorrectOperationException {
        PsiElement element = getElement(editor, psiFile);
        PsiMethodCallExpression psiMethodCallExpression = (PsiMethodCallExpression) getContainingElement(element, PsiMethodCallExpression.class);
        PsiExpressionList argumentList = psiMethodCallExpression.getArgumentList();
        PsiExpression[] expressions = argumentList.getExpressions();
        for (int i = 0; i < expressions.length; i++) {
            PsiExpression psiExpression = expressions[i];
            if (psiExpression instanceof PsiReferenceExpression) {
                PsiReferenceExpression psiReferenceExpression = (PsiReferenceExpression) psiExpression;
                PsiElement psiElement = psiReferenceExpression.resolve();
                if (psiElement instanceof PsiLocalVariable) {
                    PsiLocalVariable psiLocalVariable = (PsiLocalVariable) psiElement;
                    if (isUselessLocalVariable(psiLocalVariable)) {
                        PsiExpression initializer = psiLocalVariable.getInitializer();
                        psiExpression.replace(initializer.copy());
                        psiLocalVariable.delete();
                    }
                }
            }
        }
    }

    private boolean isUselessLocalVariable(PsiLocalVariable psiLocalVariable) {
        Query<PsiReference> query = ReferencesSearch.search(psiLocalVariable, psiLocalVariable.getUseScope(), true);
        Collection<PsiReference> all = query.findAll();
        if (all.size() != 1) {
            return false;
        }

        PsiExpression initializer = psiLocalVariable.getInitializer();
        if (initializer == null) {
            return false;
        }
        if (initializer instanceof PsiReferenceExpression || initializer instanceof PsiLiteralExpression) {
            return true;
        }
        PsiElement methodCallReference = all.iterator().next().getElement();
        PsiCodeBlock initializerBlock = getCodeBlock(psiLocalVariable);
        PsiCodeBlock methodCallBlock = getCodeBlock(methodCallReference);
        return initializerBlock.equals(methodCallBlock);
    }

    private PsiCodeBlock getCodeBlock(PsiElement element) {
        while(element != null && !(element instanceof PsiCodeBlock)) {
            element = element.getParent();
        }
        return (PsiCodeBlock) element;
    }
}
